/*
 * Copyright (c) 1997, 1998, 2003, 2006 Aleksandar Samardzic
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <assert.h>		/* for assert() function */
#include <stdlib.h>		/* for malloc() function */
#include "nff_reader.h"
#include "nff.h"		/* for NFF data structures */

/* NFFReader_Init - NFFReader class constructor */
void
NFFReader_Init(this, pBuilder)
     NFFReader      *this;
     NFFBuilder     *pBuilder;
{
	this->pBuilder = pBuilder;
}

/* NFFReader_ParseNFF - parse NFF format and build the scene */
void
NFFReader_ParseNFF(this)
     NFFReader      *this;
{
	NFFBuilder     *pBuilder = this->pBuilder;
	int             c;
	NFFBackgroundColor nffBackgroundColor;
	NFFCylCone      nffCylCone;
	NFFLight        nffLight;
	NFFMaterial     nffMaterial;
	NFFPolygon      nffPolygon;
	NFFPolygonalPatch nffPolygonalPatch;
	NFFSphere       nffSphere;
	NFFViewpoint    nffViewpoint;
	int             vertex;
	NFFPoint       *pVertex,
	               *pNormal;

	/* ask builder to perform preprocessing */
	pBuilder->PreBuild(pBuilder);

	/* for each element of NFF format, create corresponding NFF object 
	 * and pass it to builder to perform appropriate action */
	while ((c = getc(stdin)) != EOF)
		switch (c) {
		case '#':	/* comment */
			for (; c != EOF && c != '\n'; c = getc(stdin))	/* skip 
									 * the * comment */
				;
			break;

		case 'b':	/* background color */
			scanf("%g%g%g",	/* read color */
			      &(nffBackgroundColor.r),
			      &(nffBackgroundColor.g),
			      &(nffBackgroundColor.b));
			pBuilder->BuildBackgroundColor(pBuilder,
						       &nffBackgroundColor);
			break;

		case 'c':	/* cylinder or cone */
			scanf("%g%g%g%g",	/* read base */
			      &(nffCylCone.base.x), &(nffCylCone.base.y),
			      &(nffCylCone.base.z),
			      &(nffCylCone.base_radius));
			scanf("%g%g%g%g",	/* read apex */
			      &(nffCylCone.apex.x), &(nffCylCone.apex.y),
			      &(nffCylCone.apex.z),
			      &(nffCylCone.apex_radius));
			pBuilder->BuildCylCone(pBuilder, &nffCylCone);
			break;

		case 'f':	/* material */
			scanf("%g%g%g",	/* read color */
			      &(nffMaterial.color.r),
			      &(nffMaterial.color.g),
			      &(nffMaterial.color.b));
			scanf("%g", &(nffMaterial.Kd));	/* read diffuse
							 * coefficient */
			scanf("%g", &(nffMaterial.Ks));	/* read specular
							 * coefficient */
			scanf("%g", &(nffMaterial.Shine));	/* read
								 * shine
								 * power */
			scanf("%g", &(nffMaterial.T));	/* read
							 * transparency
							 * coefficient */
			scanf("%g", &(nffMaterial.index_of_refraction));	/* read 
										 * index 
										 * of 
										 * refraction 
										 */
			pBuilder->BuildMaterial(pBuilder, &nffMaterial);
			break;

		case 'l':	/* light */
			scanf("%g%g%g",	/* read position */
			      &(nffLight.position.x),
			      &(nffLight.position.y),
			      &(nffLight.position.z));
			if (scanf("%g%g%g",	/* read color */
				  &(nffLight.color.r), &(nffLight.color.g),
				  &(nffLight.color.b)) != 3) {
				nffLight.color.r = nffLight.color.g =
				    nffLight.color.b = 1;
			}
			pBuilder->BuildLight(pBuilder, &nffLight);
			break;

		case 'p':
			if ((c = getc(stdin)) != 'p') {	/* polygon */
				scanf("%d", &(nffPolygon.numVertices));	/* read 
									 * number 
									 * of 
									 * vertices 
									 */
				nffPolygon.pVertices =
				    malloc(nffPolygon.numVertices *
					   sizeof(NFFPoint));
				assert(nffPolygon.pVertices != NULL);
				for (vertex = 0, pVertex =
				     nffPolygon.pVertices;
				     vertex < nffPolygon.numVertices;
				     vertex++, pVertex++)
					scanf("%g%g%g",	/* read vertex */
					      &(pVertex->x), &(pVertex->y),
					      &(pVertex->z));
				pBuilder->BuildPolygon(pBuilder,
						       &nffPolygon);
				free(nffPolygon.pVertices);
			} else {	/* polygonal patch */

				scanf("%d", &(nffPolygonalPatch.numVertices));	/* read 
										 * number 
										 * of 
										 * vertices 
										 */
				nffPolygonalPatch.pVertices =
				    malloc(nffPolygonalPatch.numVertices *
					   sizeof(NFFPoint));
				assert(nffPolygonalPatch.pVertices !=
				       NULL);
				nffPolygonalPatch.pNormals =
				    malloc(nffPolygonalPatch.numVertices *
					   sizeof(NFFPoint));
				assert(nffPolygonalPatch.pNormals != NULL);
				for (vertex = 0, pVertex =
				     nffPolygonalPatch.pVertices, pNormal =
				     nffPolygonalPatch.pNormals;
				     vertex <
				     nffPolygonalPatch.numVertices;
				     vertex++, pVertex++, pNormal++) {
					scanf("%g%g%g",	/* read * vertex */
					      &(pVertex->x), &(pVertex->y),
					      &(pVertex->z));
					scanf("%g%g%g",	/* read * normal */
					      &(pNormal->x), &(pNormal->y),
					      &(pNormal->z));
				}
				pBuilder->BuildPolygonalPatch(pBuilder,
							      &nffPolygonalPatch);
				free(nffPolygonalPatch.pVertices);
				free(nffPolygonalPatch.pNormals);
			}
			break;

		case 's':	/* sphere */
			scanf("%g%g%g",	/* read center */
			      &(nffSphere.center.x), &(nffSphere.center.y),
			      &(nffSphere.center.z));
			scanf("%g", &(nffSphere.radius));	/* read
								 * radius */
			pBuilder->BuildSphere(pBuilder, &nffSphere);
			break;

		case 'v':	/* viewpoint */
			/* read from point */
			for (; getc(stdin) != 'f';);
			scanf("rom%g%g%g", &(nffViewpoint.from.x),
			      &(nffViewpoint.from.y),
			      &(nffViewpoint.from.z));

			/* read at point */
			for (; getc(stdin) != 'a';);
			scanf("t%g%g%g", &(nffViewpoint.at.x),
			      &(nffViewpoint.at.y), &(nffViewpoint.at.z));

			/* read up vector */
			for (; getc(stdin) != 'u';);
			scanf("p%g%g%g", &(nffViewpoint.up.x),
			      &(nffViewpoint.up.y), &(nffViewpoint.up.z));

			/* read field of view angle */
			for (; getc(stdin) != 'a';);
			scanf("ngle%g", &(nffViewpoint.angle));

			/* read hither distance */
			for (; getc(stdin) != 'h';);
			scanf("ither%g", &(nffViewpoint.hither));

			/* read image resolution */
			for (; getc(stdin) != 'r';);
			scanf("esolution%d%d", &(nffViewpoint.xres),
			      &(nffViewpoint.yres));

			/* set default aspect ratio */
			nffViewpoint.aspect_ratio = 1;

			pBuilder->BuildViewpoint(pBuilder, &nffViewpoint);
			break;
		}

	/* ask builder to perform postprocessing */
	pBuilder->PostBuild(pBuilder);
}
